package lang

import destination.*
import source.*

import com.burtbeckwith.gorm.GormHelper;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.codehaus.groovy.grails.plugins.orm.hibernate.*
import org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin

import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.transaction.TransactionStatus;

import grails.spring.BeanBuilder

import org.apache.log4j.*;

import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.codehaus.groovy.grails.commons.DefaultGrailsApplication



public class XportDsl{

	def log
	static void main(String[] args){
		
		def error = 0
		
		if(args.size() == 0){
			println "file required"
			return
		}
		
		def file = new File(args[0])

		if(!file.exists()){
			def userdir = System.properties['user.dir']
			println "Looking for in ${System.properties['user.dir']}${File.separator}${args[0]}"
			file = new File("${System.properties['user.dir']}${File.separator}${args[0]}")
			
			if(!file.exists()){
				println "Looking for in ${args[0]}"
				file = new File("${args[0]}")
				
				if(!file.exists()){
					println "Could not find the file ${args[0]}"
					
					error = 1
				}
			}			
		}
		
		
	
		if(error == 0){
			try{
				def xportdsl = new XportDsl()
		
				xportdsl.runExportTableRules(file)
			}catch(e){
				error = 1
			}
		}
		
		System.exit(error)
		return
	}
	
	private configuration(application, file){
		def configurationFile = new GrailsAnnotationConfiguration(
				grailsApplication: application)
				
		loadProperties(configurationFile, file)
		def sessionFactory = configurationFile.buildSessionFactory()
	}
	
	private void loadProperties(configurationFile, file) {
		Properties properties = new Properties()

		def dsConfig = new ConfigSlurper().parse(new File(file).toURL())
		properties.'hibernate.connection.username' = dsConfig.dataSource.username
		properties.'hibernate.connection.password' = dsConfig.dataSource.password
		properties.'hibernate.connection.url' = dsConfig.dataSource.url
		properties.'hibernate.connection.driver_class' = dsConfig.dataSource.driverClassName

		if (dsConfig.dataSource.dbCreate) {
			properties.'hibernate.hbm2ddl.auto' = dsConfig.dataSource.dbCreate
		}

		def dialect = dsConfig.dataSource.dialect
		if (dialect instanceof Class) {
			dialect = dialect.name
		}
		properties.'hibernate.dialect' = dialect // no autodetect, must set

		configurationFile.properties = properties
	}

	private void loadContext(){
	
		def appDirectory = new File(getClass().protectionDomain.codeSource.location.path).parent
		println "app directory ${appDirectory}"
		
		def ctx = new FileSystemXmlApplicationContext("${appDirectory}${File.separator}applicationContextSource.xml");
		
		println "loading application context source"
		
		System.setProperty("appDirectory", appDirectory);
		ctx = new FileSystemXmlApplicationContext("${appDirectory}${File.separator}applicationContextDestination.xml");
		
		println "loading application context destination"
		
		if(Logger?.getRootLogger() == null){
			println "Die log"
			System.exit(1)
		}
		
		log = Logger.getRootLogger()
		
		println "log initialized"
		
		if(!log){
			println "throwing exception"
			throw new Exception("Log not configured")
			System.exit(1)
		}
		
		def tMgr = GormHelper.getTransactionManager();
		def trans = tMgr.getTransaction(null);
		def exception = false
	
	}

	void runExportTableRules(File dslFile){
		loadContext()
	
		if(dslFile.isDirectory()){
			dslFile.eachFile{dsl ->
				processFile(dsl)
			}
		}else{
			processFile(dslFile)
		}
	}
	
	void runExportTableRules(String dslPath){
		def dsl = new File(dslPath)
		runExportTableRules(dsl)
	}
	
	
	private void processFile(File dsl){
		def dslScript = new GroovyShell().parse(dsl.text)

		dslScript.metaClass = createEMC(dslScript.class, {ExpandoMetaClass emc ->
			emc.export_table = { Closure cl ->
					cl.delegate = new ExportTableDelegate(log)
					cl.resolveStrategy = Closure.DELEGATE_FIRST
					
					cl()
			}
		})
		dslScript.run()
	}	
	
	private ExpandoMetaClass createEMC(Class clazz, Closure cl){
		def emc = new ExpandoMetaClass(clazz, false)
		
		cl(emc)
		
		emc.initialize()
		
		return emc
	}
}

class ExportTableDelegate{
	def fromTable
	def toTable
	def copy = [:]
	def copyClosure = [:]
	def criteria
	def copy_fields = []
	def copySpecialClosure = [:]
	def log
	
	ExportTableDelegate(log){
		this.log = log
		
		//Add value method to string
		String.metaClass.value = {cl ->	
			//Execute closure
			copyClosure[delegate] = cl
		}
		
		//Add value method to string
		String.metaClass.convert = {cl ->	
			//Execute closure
			[delegate,  cl]
		}


	}

	void from(from){
		fromTable = from
	}
	
	void to(to){
		toTable = to
	}
	
	void copy_column(Closure cl) {
		cl.delegate = new CopyColumnDelegate()
		cl.resolveStrategy = Closure.DELEGATE_FIRST
		cl()
		copy[cl.to] = cl.from
		copy.keySet().removeAll(copyClosure.keySet())
	}
	
	void setCopy_fields(fields) {
		copy_fields(fields)
	}
	
	void copy_fields(fields) {
		fields.each{it ->
			//Here the value will be the key of the toTableInstance
			if(it.key instanceof Closure){
				copyClosure[it.value] = it.key
			}
			else if(it.key instanceof List){
				copySpecialClosure[it.value] = it.key
				
			}else{
				copy[it.value] = it.key
			}
		}
		
	}
	
	void where(cl){
		criteria = cl
	}
	
	
	void execute(){
		if(copy || copyClosure || copySpecialClosure){
			if(criteria){
				withCriteria()
			}else{
				all()
			}
		}
	}
	
	private withCriteria(){
		fromTable.createCriteria().list(criteria).each{fromTableInstance -> 
			processData(fromTableInstance)
		}
	}
	
	private all(){
		fromTable.list().each{fromTableInstance ->
			processData(fromTableInstance)
		}
	}
	
	private processData(fromTableInstance){
		def toTableInstance = toTable.newInstance()
		
		copy.each{
			def value = fromTableInstance."${it.value}"
			toTableInstance."${it.key}" = value
			log.info "${it.key} : " + toTableInstance."${it.key}"
		}
	
		copyClosure.each{cl ->
			def value = cl.value(fromTableInstance, toTableInstance)
			toTableInstance."${cl.key}" = value
			log.info "${cl.key} : " + toTableInstance."${cl.key}"
		}
		
		copySpecialClosure.each{cl ->
			def value = cl.value[1](fromTableInstance."${cl.value[0]}")
			toTableInstance."${cl.key}" = value
			log.info "${cl.key} : " + toTableInstance."${cl.key}"
			
		}
		toTableInstance.save()
	}
}


class CopyColumnDelegate{

	def from
	def to
	
	CopyColumnDelegate() {
	}


	void from(String from){
		this.from = from
	}
	
	void to(String to){
		this.to = to
	}

}
